"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = createListComponent;
exports.getRectSize = getRectSize;
exports.isHorizontalFunc = isHorizontalFunc;
exports.isRtlFunc = isRtlFunc;

var _taro = _interopRequireDefault(require("@tarojs/taro"));

var _memoize = require("../memoize");

var _react = require("react");

var _timer = require("../timer");

var _domHelpers = require("../domHelpers");

var _excluded = ["children", "className", "direction", "height", "innerRef", "innerElementType", "innerTagName", "itemElementType", "itemTagName", "itemCount", "itemData", "itemKey", "layout", "outerElementType", "outerTagName", "style", "useIsScrolling", "width", "position", "renderBottom"];

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }

function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }

function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }

function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }

function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }

function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }

function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }

function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } else if (call !== void 0) { throw new TypeError("Derived constructors may only return object or undefined"); } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); return true; } catch (e) { return false; } }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

var IS_SCROLLING_DEBOUNCE_INTERVAL = 150;

var defaultItemKey = function defaultItemKey(index) {
  return index;
}; // In DEV mode, this Set helps us only log a warning once per component instance.
// This avoids spamming the console every time a render happens.


var INSTANCE_ID = 0;

function isHorizontalFunc(_ref) {
  var direction = _ref.direction,
      layout = _ref.layout;
  return direction === 'horizontal' || layout === 'horizontal';
}

function isRtlFunc(_ref2) {
  var direction = _ref2.direction;
  return direction === 'rtl';
}

function getRectSize(id) {
  var success = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
  var fail = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () {};

  var query = _taro.default.createSelectorQuery();

  query.select(id).fields({
    size: true
  }, function (res) {
    if (res) {
      success(res);
    } else {
      fail();
    }
  }).exec();
}

function createListComponent(_ref3) {
  var getItemOffset = _ref3.getItemOffset,
      getEstimatedTotalSize = _ref3.getEstimatedTotalSize,
      getItemSize = _ref3.getItemSize,
      getOffsetForIndexAndAlignment = _ref3.getOffsetForIndexAndAlignment,
      getStartIndexForOffset = _ref3.getStartIndexForOffset,
      getStopIndexForStartIndex = _ref3.getStopIndexForStartIndex,
      initInstanceProps = _ref3.initInstanceProps,
      shouldResetStyleCacheOnItemSizeChange = _ref3.shouldResetStyleCacheOnItemSizeChange,
      validateProps = _ref3.validateProps;

  var _class;

  var _temp;

  return _temp = _class = /*#__PURE__*/function (_PureComponent) {
    _inherits(List, _PureComponent);

    var _super = _createSuper(List);

    // Always use explicit constructor for React components.
    // It produces less code after transpilation. (#26)
    // eslint-disable-next-line no-useless-constructor
    function List(props) {
      var _this;

      _classCallCheck(this, List);

      _this = _super.call(this, props);
      _this._instanceProps = initInstanceProps(_this.props, _assertThisInitialized(_this));
      _this._outerRef = void 0;
      _this._resetIsScrollingTimeoutId = null;
      _this.state = {
        id: _this.props.id || "virtual-list-".concat(INSTANCE_ID++),
        instance: _assertThisInitialized(_this),
        isScrolling: false,
        scrollDirection: 'forward',
        scrollOffset: typeof _this.props.initialScrollOffset === 'number' ? _this.props.initialScrollOffset : 0,
        scrollUpdateWasRequested: false,
        sizeList: []
      };

      if (_this.props.unlimitedSize) {
        _this.state.sizeList = new Array(_this.props.itemCount).fill(-1);
      }

      _this.field = {
        scrollLeft: 0,
        scrollTop: 0,
        scrollHeight: 0,
        scrollWidth: 0,
        clientHeight: 0,
        clientWidth: 0
      };
      _this._callOnItemsRendered = void 0;
      _this._callOnItemsRendered = (0, _memoize.memoizeOne)(function (overscanStartIndex, overscanStopIndex, visibleStartIndex, visibleStopIndex) {
        return _this.props.onItemsRendered({
          overscanStartIndex: overscanStartIndex,
          overscanStopIndex: overscanStopIndex,
          visibleStartIndex: visibleStartIndex,
          visibleStopIndex: visibleStopIndex
        });
      });
      _this._callOnScroll = void 0;
      _this._callOnScroll = (0, _memoize.memoizeOne)(function (scrollDirection, scrollOffset, scrollUpdateWasRequested, detail) {
        return _this.props.onScroll({
          scrollDirection: scrollDirection,
          scrollOffset: scrollOffset,
          scrollUpdateWasRequested: scrollUpdateWasRequested,
          detail: detail
        });
      });
      _this._getSize = void 0;

      _this._getSize = function (size) {
        if (typeof size === 'number' && size >= 0) {
          return size;
        }

        return _this.props.itemSize;
      };

      _this._getSizeUploadSync = void 0;

      _this._getSizeUploadSync = function (index, isHorizontal) {
        var ID = "#".concat(_this.state.id, "-").concat(index);
        return new Promise(function (resolve) {
          var success = function success(_ref4) {
            var width = _ref4.width,
                height = _ref4.height;
            var sizeList = _this.state.sizeList;
            var size = isHorizontal ? width : height;

            if (size !== sizeList[index]) {
              sizeList[index] = _this._getSize(size);

              _this.setState({
                sizeList: _toConsumableArray(sizeList)
              }, function () {
                resolve(_this._getSize(size));
              });
            }
          };

          var fail = function fail() {
            var _this$_getRangeToRend = _this._getRangeToRender(),
                _this$_getRangeToRend2 = _slicedToArray(_this$_getRangeToRend, 2),
                startIndex = _this$_getRangeToRend2[0],
                stopIndex = _this$_getRangeToRend2[1];

            if (index >= startIndex && index <= stopIndex) {
              setTimeout(function () {
                getRectSize(ID, success, fail);
              }, 100);
            }
          };

          getRectSize(ID, success, fail);
        });
      };

      _this._getSizeUpload = function (index, isHorizontal) {
        _this._getSizeUploadSync(index, isHorizontal);

        var sizeList = _this.state.sizeList;
        return _this._getSize(sizeList[index]);
      };

      _this._getCountSize = void 0;

      _this._getCountSize = function (props, count) {
        if (!props.unlimitedSize) {
          return props.itemSize * count;
        }

        var sizeList = _this.state.sizeList;
        var sizes = sizeList.slice(0, count);
        return sizes.reduce(function (p, a) {
          return p + _this._getSize(a);
        }, 0);
      };

      _this._getSizeCount = void 0;

      _this._getSizeCount = function (props, offset) {
        if (offset === 0) {
          return 0;
        }

        if (!props.unlimitedSize) {
          return Math.min(props.itemCount - 1, Math.floor(offset / props.itemSize));
        }

        var offsetSize = 0;
        var sizeList = _this.state.sizeList;
        var count = sizeList.reduce(function (p, a) {
          a = _this._getSize(a);

          if (offsetSize < offset) {
            offsetSize += a;
            return ++p;
          }

          return p;
        }, 0);
        return count - 1;
      };

      _this._getStyleValue = function (value) {
        return typeof value === 'number' ? "".concat(value, "px") : value == null ? '' : value;
      };

      _this._getItemStyle = void 0;

      _this._getItemStyle = function (index) {
        var _this$props = _this.props,
            direction = _this$props.direction,
            itemSize = _this$props.itemSize,
            layout = _this$props.layout;

        var itemStyleCache = _this._getItemStyleCache(shouldResetStyleCacheOnItemSizeChange && itemSize, shouldResetStyleCacheOnItemSizeChange && layout, shouldResetStyleCacheOnItemSizeChange && direction);

        var style;
        var offset = getItemOffset(_this.props, index, _assertThisInitialized(_this));
        var size = getItemSize(_this.props, index, _assertThisInitialized(_this)); // TODO Deprecate direction "horizontal"

        var isHorizontal = isHorizontalFunc(_this.props);
        var isRtl = isRtlFunc(_this.props);

        if (itemStyleCache.hasOwnProperty(index)) {
          style = itemStyleCache[index];

          if (isHorizontal) {
            style.width = size;

            if (isRtl) {
              style.right = offset;
            } else {
              style.left = offset;
            }
          } else {
            style.height = size;
            style.top = offset;
          }
        } else {
          var offsetHorizontal = isHorizontal ? offset : 0;
          itemStyleCache[index] = style = {
            position: 'absolute',
            left: !isRtl ? offsetHorizontal : undefined,
            right: isRtl ? offsetHorizontal : undefined,
            top: !isHorizontal ? offset : 0,
            height: !isHorizontal ? size : '100%',
            width: isHorizontal ? size : '100%'
          };
        }

        for (var k in style) {
          if (style.hasOwnProperty(k)) {
            style[k] = _this._getStyleValue(style[k]);
          }
        }

        return style;
      };

      _this._getItemStyleCache = void 0;
      _this._getItemStyleCache = (0, _memoize.memoizeOne)(function () {
        return {};
      });

      _this._onScrollHorizontal = function (event) {
        var _event$currentTarget = event.currentTarget,
            clientWidth = _event$currentTarget.clientWidth,
            scrollTop = _event$currentTarget.scrollTop,
            scrollLeft = _event$currentTarget.scrollLeft,
            scrollHeight = _event$currentTarget.scrollHeight,
            scrollWidth = _event$currentTarget.scrollWidth;
        _this.field.scrollHeight = scrollHeight;
        _this.field.scrollWidth = getEstimatedTotalSize(_this.props, _assertThisInitialized(_this));
        _this.field.scrollTop = scrollTop;
        _this.field.scrollLeft = scrollLeft;
        _this.field.clientHeight = scrollHeight;
        _this.field.clientWidth = clientWidth;

        _this.setState(function (prevState) {
          if (prevState.scrollOffset === scrollLeft) {
            // Scroll position may have been updated by cDM/cDU,
            // In which case we don't need to trigger another render,
            // And we don't want to update state.isScrolling.
            return null;
          }

          var scrollOffset = scrollLeft;

          if (isRtlFunc(_this.props)) {
            // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
            // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
            // It's also easier for this component if we convert offsets to the same format as they would be in for ltr.
            // So the simplest solution is to determine which browser behavior we're dealing with, and convert based on it.
            switch ((0, _domHelpers.getRTLOffsetType)()) {
              case 'negative':
                scrollOffset = -scrollLeft;
                break;

              case 'positive-descending':
                scrollOffset = scrollWidth - clientWidth - scrollLeft;
                break;
            }
          } // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.


          scrollOffset = Math.max(0, Math.min(scrollOffset, scrollWidth - clientWidth));
          _this.field.scrollWidth = scrollOffset;
          return {
            isScrolling: true,
            scrollDirection: prevState.scrollOffset < scrollLeft ? 'forward' : 'backward',
            scrollOffset: scrollOffset,
            scrollUpdateWasRequested: false
          };
        }, _this._resetIsScrollingDebounced);
      };

      _this._onScrollVertical = function (event) {
        var _event$currentTarget2 = event.currentTarget,
            clientHeight = _event$currentTarget2.clientHeight,
            scrollHeight = _event$currentTarget2.scrollHeight,
            scrollWidth = _event$currentTarget2.scrollWidth,
            scrollTop = _event$currentTarget2.scrollTop,
            scrollLeft = _event$currentTarget2.scrollLeft;
        _this.field.scrollHeight = getEstimatedTotalSize(_this.props, _assertThisInitialized(_this));
        _this.field.scrollWidth = scrollWidth;
        _this.field.scrollTop = scrollTop;
        _this.field.scrollLeft = scrollLeft;
        _this.field.clientHeight = clientHeight;
        _this.field.clientWidth = scrollWidth;

        _this.setState(function (prevState) {
          if (prevState.scrollOffset === scrollTop) {
            // Scroll position may have been updated by cDM/cDU,
            // In which case we don't need to trigger another render,
            // And we don't want to update state.isScrolling.
            return null;
          } // Prevent Safari's elastic scrolling from causing visual shaking when scrolling past bounds.


          var scrollOffset = Math.max(0, Math.min(scrollTop, scrollHeight - clientHeight));
          _this.field.scrollTop = scrollOffset;
          return {
            isScrolling: true,
            scrollDirection: prevState.scrollOffset < scrollOffset ? 'forward' : 'backward',
            scrollOffset: scrollOffset,
            scrollUpdateWasRequested: false
          };
        }, _this._resetIsScrollingDebounced);
      };

      _this._outerRefSetter = function (ref) {
        var outerRef = _this.props.outerRef;
        _this._outerRef = ref;

        if (typeof outerRef === 'function') {
          outerRef(ref);
        } else if (outerRef != null && _typeof(outerRef) === 'object' && outerRef.hasOwnProperty('current')) {
          outerRef.current = ref;
        }
      };

      _this._resetIsScrollingDebounced = function () {
        if (_this._resetIsScrollingTimeoutId !== null) {
          (0, _timer.cancelTimeout)(_this._resetIsScrollingTimeoutId);
        }

        _this._resetIsScrollingTimeoutId = (0, _timer.requestTimeout)(_this._resetIsScrolling, IS_SCROLLING_DEBOUNCE_INTERVAL);
      };

      _this._resetIsScrolling = function () {
        _this._resetIsScrollingTimeoutId = null;

        _this.setState({
          isScrolling: false
        }, function () {
          // Clear style cache after state update has been committed.
          // This way we don't break pure sCU for items that don't use isScrolling param.
          _this._getItemStyleCache(-1, null);
        });
      };

      return _this;
    }

    _createClass(List, [{
      key: "scrollTo",
      value: function scrollTo(scrollOffset) {
        scrollOffset = Math.max(0, scrollOffset);
        this.setState(function (prevState) {
          if (prevState.scrollOffset === scrollOffset) {
            return null;
          }

          return {
            scrollDirection: prevState.scrollOffset < scrollOffset ? 'forward' : 'backward',
            scrollOffset: scrollOffset,
            scrollUpdateWasRequested: true
          };
        }, this._resetIsScrollingDebounced);
      }
    }, {
      key: "scrollToItem",
      value: function scrollToItem(index) {
        var align = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'auto';
        var itemCount = this.props.itemCount;
        var scrollOffset = this.state.scrollOffset;
        index = Math.max(0, Math.min(index, itemCount - 1));
        this.scrollTo(getOffsetForIndexAndAlignment(this.props, this.state.id, index, align, scrollOffset, this));
      }
    }, {
      key: "componentDidMount",
      value: function componentDidMount() {
        var initialScrollOffset = this.props.initialScrollOffset;

        if (typeof initialScrollOffset === 'number' && this._outerRef != null) {
          var outerRef = this._outerRef; // TODO Deprecate direction "horizontal"

          if (isHorizontalFunc(this.props)) {
            outerRef.scrollLeft = initialScrollOffset;
          } else {
            outerRef.scrollTop = initialScrollOffset;
          }
        }

        this._callPropsCallbacks();
      }
    }, {
      key: "componentDidUpdate",
      value: function componentDidUpdate() {
        var _this$state = this.state,
            scrollOffset = _this$state.scrollOffset,
            scrollUpdateWasRequested = _this$state.scrollUpdateWasRequested;

        if (scrollUpdateWasRequested && this._outerRef != null) {
          var outerRef = this._outerRef; // TODO Deprecate direction "horizontal"

          if (isHorizontalFunc(this.props)) {
            if (isRtlFunc(this.props)) {
              // TRICKY According to the spec, scrollLeft should be negative for RTL aligned elements.
              // This is not the case for all browsers though (e.g. Chrome reports values as positive, measured relative to the left).
              // So we need to determine which browser behavior we're dealing with, and mimic it.
              switch ((0, _domHelpers.getRTLOffsetType)()) {
                case 'negative':
                  outerRef.scrollLeft = -scrollOffset;
                  break;

                case 'positive-ascending':
                  outerRef.scrollLeft = scrollOffset;
                  break;

                default:
                  var clientWidth = outerRef.clientWidth,
                      scrollWidth = outerRef.scrollWidth;
                  outerRef.scrollLeft = scrollWidth - clientWidth - scrollOffset;
                  break;
              }
            } else {
              outerRef.scrollLeft = scrollOffset;
            }
          } else {
            outerRef.scrollTop = scrollOffset;
          }
        }

        this._callPropsCallbacks();
      }
    }, {
      key: "componentWillUnmount",
      value: function componentWillUnmount() {
        if (this._resetIsScrollingTimeoutId !== null) {
          (0, _timer.cancelTimeout)(this._resetIsScrollingTimeoutId);
        }
      }
    }, {
      key: "render",
      value: function render() {
        var _this$props2 = this.props,
            children = _this$props2.children,
            className = _this$props2.className,
            direction = _this$props2.direction,
            height = _this$props2.height,
            innerRef = _this$props2.innerRef,
            innerElementType = _this$props2.innerElementType,
            innerTagName = _this$props2.innerTagName,
            itemElementType = _this$props2.itemElementType,
            itemTagName = _this$props2.itemTagName,
            itemCount = _this$props2.itemCount,
            itemData = _this$props2.itemData,
            _this$props2$itemKey = _this$props2.itemKey,
            itemKey = _this$props2$itemKey === void 0 ? defaultItemKey : _this$props2$itemKey,
            layout = _this$props2.layout,
            outerElementType = _this$props2.outerElementType,
            outerTagName = _this$props2.outerTagName,
            style = _this$props2.style,
            useIsScrolling = _this$props2.useIsScrolling,
            width = _this$props2.width,
            position = _this$props2.position,
            renderBottom = _this$props2.renderBottom,
            rest = _objectWithoutProperties(_this$props2, _excluded);

        var _this$state2 = this.state,
            id = _this$state2.id,
            isScrolling = _this$state2.isScrolling,
            scrollOffset = _this$state2.scrollOffset,
            scrollUpdateWasRequested = _this$state2.scrollUpdateWasRequested; // TODO Deprecate direction "horizontal"

        var isHorizontal = isHorizontalFunc(this.props);
        var onScroll = isHorizontal ? this._onScrollHorizontal : this._onScrollVertical;

        var _this$_getRangeToRend3 = this._getRangeToRender(),
            _this$_getRangeToRend4 = _slicedToArray(_this$_getRangeToRend3, 2),
            startIndex = _this$_getRangeToRend4[0],
            stopIndex = _this$_getRangeToRend4[1];

        var items = [];

        if (itemCount > 0) {
          for (var index = startIndex; index <= stopIndex; index++) {
            var key = itemKey(index, itemData);

            var _style = void 0;

            if (position === 'relative') {
              var size = getItemSize(this.props, index, this);
              _style = {
                height: this._getStyleValue(!isHorizontal ? size : '100%'),
                width: this._getStyleValue(isHorizontal ? size : '100%')
              };
            } else {
              _style = this._getItemStyle(index);
            }

            items.push( /*#__PURE__*/(0, _react.createElement)(itemElementType || itemTagName || 'div', {
              key: key,
              style: _style
            }, /*#__PURE__*/(0, _react.createElement)(children, {
              id: "".concat(id, "-").concat(index),
              data: itemData,
              index: index,
              isScrolling: useIsScrolling ? isScrolling : undefined
            })));
          }
        } // Read this value AFTER items have been created,
        // So their actual sizes (if variable) are taken into consideration.


        var estimatedTotalSize = getEstimatedTotalSize(this.props, this);

        var outerElementProps = _objectSpread(_objectSpread({}, rest), {}, {
          id: id,
          className: className,
          onScroll: onScroll,
          ref: this._outerRefSetter,
          layout: layout,
          style: _objectSpread({
            position: 'relative',
            height: this._getStyleValue(height),
            width: this._getStyleValue(width),
            overflow: 'auto',
            WebkitOverflowScrolling: 'touch',
            willChange: 'transform',
            direction: direction
          }, style)
        });

        if (scrollUpdateWasRequested) {
          if (isHorizontal) {
            outerElementProps.scrollLeft = scrollOffset;
          } else {
            outerElementProps.scrollTop = scrollOffset;
          }
        }

        if (position === 'relative') {
          var pre = getItemOffset(this.props, startIndex, this);
          return /*#__PURE__*/(0, _react.createElement)(outerElementType || outerTagName || 'div', outerElementProps, /*#__PURE__*/(0, _react.createElement)(itemElementType || itemTagName || 'div', {
            key: "".concat(id, "-pre"),
            id: "".concat(id, "-pre"),
            style: {
              height: isHorizontal ? '100%' : this._getStyleValue(pre),
              width: !isHorizontal ? '100%' : this._getStyleValue(pre)
            }
          }), /*#__PURE__*/(0, _react.createElement)(innerElementType || innerTagName || 'div', {
            ref: innerRef,
            key: "".concat(id, "-inner"),
            id: "".concat(id, "-inner"),
            style: {
              pointerEvents: isScrolling ? 'none' : 'auto'
            }
          }, items), renderBottom);
        }

        return /*#__PURE__*/(0, _react.createElement)(outerElementType || outerTagName || 'div', outerElementProps, /*#__PURE__*/(0, _react.createElement)(innerElementType || innerTagName || 'div', {
          ref: innerRef,
          key: "".concat(id, "-inner"),
          id: "".concat(id, "-inner"),
          style: {
            height: this._getStyleValue(isHorizontal ? '100%' : estimatedTotalSize),
            pointerEvents: isScrolling ? 'none' : 'auto',
            width: this._getStyleValue(isHorizontal ? estimatedTotalSize : '100%')
          }
        }, items), renderBottom);
      }
    }, {
      key: "_callPropsCallbacks",
      value: function _callPropsCallbacks() {
        if (typeof this.props.onItemsRendered === 'function') {
          var itemCount = this.props.itemCount;

          if (itemCount > 0) {
            var _this$_getRangeToRend5 = this._getRangeToRender(),
                _this$_getRangeToRend6 = _slicedToArray(_this$_getRangeToRend5, 4),
                overscanStartIndex = _this$_getRangeToRend6[0],
                overscanStopIndex = _this$_getRangeToRend6[1],
                visibleStartIndex = _this$_getRangeToRend6[2],
                visibleStopIndex = _this$_getRangeToRend6[3];

            this._callOnItemsRendered(overscanStartIndex, overscanStopIndex, visibleStartIndex, visibleStopIndex);
          }
        }

        if (typeof this.props.onScroll === 'function') {
          var _this$state3 = this.state,
              scrollDirection = _this$state3.scrollDirection,
              scrollOffset = _this$state3.scrollOffset,
              scrollUpdateWasRequested = _this$state3.scrollUpdateWasRequested;

          this._callOnScroll(scrollDirection, scrollOffset, scrollUpdateWasRequested, this.field);
        }
      } // Lazily create and cache item styles while scrolling,
      // So that pure component sCU will prevent re-renders.
      // We maintain this cache, and pass a style prop rather than index,
      // So that List can clear cached styles and force item re-render if necessary.

    }, {
      key: "_getRangeToRender",
      value: function _getRangeToRender() {
        var _this$props3 = this.props,
            itemCount = _this$props3.itemCount,
            overscanCount = _this$props3.overscanCount;
        var _this$state4 = this.state,
            isScrolling = _this$state4.isScrolling,
            scrollDirection = _this$state4.scrollDirection,
            scrollOffset = _this$state4.scrollOffset;

        if (itemCount === 0) {
          return [0, 0, 0, 0];
        }

        var startIndex = getStartIndexForOffset(this.props, scrollOffset, this);
        var stopIndex = getStopIndexForStartIndex(this.props, scrollOffset, startIndex, this); // Overscan by one item in each direction so that tab/focus works.
        // If there isn't at least one extra item, tab loops back around.

        var overscanBackward = !isScrolling || scrollDirection === 'backward' ? Math.max(1, overscanCount) : 1;
        var overscanForward = !isScrolling || scrollDirection === 'forward' ? Math.max(1, overscanCount) : 1;
        return [Math.max(0, startIndex - overscanBackward), Math.max(0, Math.min(itemCount - 1, stopIndex + overscanForward)), startIndex, stopIndex];
      }
    }], [{
      key: "getDerivedStateFromProps",
      value: function getDerivedStateFromProps(nextProps, prevState) {
        validateProps(nextProps, prevState);
        return null;
      }
    }]);

    return List;
  }(_react.PureComponent), _class.defaultProps = {
    direction: 'ltr',
    itemData: undefined,
    layout: 'vertical',
    overscanCount: 2,
    useIsScrolling: false
  }, _temp;
} // NOTE: I considered further wrapping individual items with a pure ListItem component.
// This would avoid ever calling the render function for the same index more than once,
// But it would also add the overhead of a lot of components/fibers.
// I assume people already do this (render function returning a class component),
// So my doing it would just unnecessarily double the wrappers.